package com.centersoft.flowable.core.web.service;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.centersoft.flowable.core.config.WorkFlowConfig;
import com.centersoft.flowable.core.constant.FlowableConstant;
import com.centersoft.flowable.core.dto.FlowDto;
import com.centersoft.flowable.core.dto.QueryAuditDocumentDto;
import com.centersoft.flowable.core.dto.TaskDefExtensionRequest;
import com.centersoft.flowable.core.model.*;
import com.centersoft.flowable.core.util.ProcessDefCache;
import com.centersoft.flowable.core.web.gateway.TaskDefExtensionGateway;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangzhiqiang
 * @since 2025/4/6 21:57
 */
@Service
@Transactional(readOnly = true)
public class FlowHistoryService {
    protected BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
    private final HistoryService historyService;

    private final TaskService taskService;

    private final WorkFlowConfig workFlowConfig;


    private final FlowTaskClientService flowTaskClientService;


    private final RepositoryService repositoryService;

    private final TaskDefExtensionGateway taskDefExtensionGateway;

    private final RuntimeService runtimeService;

    public FlowHistoryService(HistoryService historyService, TaskService taskService, WorkFlowConfig workFlowConfig, FlowTaskClientService flowTaskClientService, RepositoryService repositoryService, TaskDefExtensionGateway taskDefExtensionGateway, RuntimeService runtimeService) {
        this.historyService = historyService;
        this.taskService = taskService;
        this.workFlowConfig = workFlowConfig;
        this.flowTaskClientService = flowTaskClientService;
        this.repositoryService = repositoryService;
        this.taskDefExtensionGateway = taskDefExtensionGateway;
        this.runtimeService = runtimeService;
    }


    public Object getAuditDocumentNewForClient(QueryAuditDocumentDto request) {


        String procInsId = request.getProcInsId();

        String currentUserId = request.getCurrentUserId();
        String orgCode = request.getCurrentUserId();
        JSONObject jsonObject = new JSONObject();

        ProcessVo processVo = null;
        FlowDto flow = new FlowDto();
        if (!StringUtils.isEmpty(procInsId)) {
            // 查询历史
            List<Task> taskList = new ArrayList<>();
            //根据组织编号过滤流程,需要跟流程变量和,已经传入的组织编号进行比对,解决跨组织仍然阔以查找出用户的数据
            if (StringUtils.isNotEmpty(orgCode) && workFlowConfig.getEnableMulitOrg()) {
                taskList = taskService
                        .createTaskQuery()
                        .processInstanceId(procInsId)
                        .taskAssignee(currentUserId)
                        .processVariableValueEquals("orgCode", orgCode)
                        .list();
            } else {
                taskList = taskService
                        .createTaskQuery()
                        .processInstanceId(procInsId)
                        .taskAssignee(currentUserId)
                        .list();
            }
            if (CollectionUtils.isEmpty(taskList)) {
                taskList = taskService.createTaskQuery().processInstanceId(procInsId).list();
            }

            // 如果没有查询到待办数据,就去查询历史数据
            if (CollectionUtils.isEmpty(taskList)) {
                HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInsId);
                HistoricProcessInstance historicProcessInstance = query.singleResult();
                try {
                    processVo = queryProcessState(
                            historicProcessInstance.getProcessDefinitionId(),
                            historicProcessInstance.getId());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                processVo.setEndTime(historicProcessInstance.getEndTime());
                processVo.setStartTime(historicProcessInstance.getStartTime());
                processVo.setProcessDefinitionId(historicProcessInstance.getProcessDefinitionId());
                processVo.setProcessInstanceId(historicProcessInstance.getId());
                processVo.setVars(historicProcessInstance.getProcessVariables());
                processVo.setProcessDefinitionName(historicProcessInstance.getProcessDefinitionName());
                processVo.setVersion(historicProcessInstance.getProcessDefinitionVersion());
                processVo.setProcDefKey(historicProcessInstance.getProcessDefinitionKey());
                // 设置流程表单信息
                if (historicProcessInstance != null && historicProcessInstance.getBusinessKey() != null
                        && historicProcessInstance.getBusinessKey().contains(":")) {
                    String[] ss = historicProcessInstance.getBusinessKey().split(":");
                    flow.setBusinessTable(ss[0]);
                    flow.setBusinessId(ss[1]);
                } else if (historicProcessInstance != null && historicProcessInstance.getBusinessKey() != null) {
                    flow.setBusinessId(historicProcessInstance.getBusinessKey());
                }
                flow.setTitle(historicProcessInstance.getProcessDefinitionName());

            } else {
                // 查询流程当前任务
                Task task = taskList.get(0);
                Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getMainProcess();
                processVo = new ProcessVo();
                TaskVo taskVo = new TaskVo(task);
                taskVo.setProcessDefKey(process.getId());
                processVo.setTask(taskVo);
                processVo.setVars(task.getProcessVariables());
                processVo.setProcessDefinitionId(taskVo.getProcessDefinitionId());
                processVo.setProcessInstanceId(procInsId);
                processVo.setProcessDefinitionName(ProcessDefCache.get(task.getProcessDefinitionId()).getName());
                processVo.setVersion(ProcessDefCache.get(task.getProcessDefinitionId()).getVersion());

                // 获取流程实例对象
                if (flow.getProcInsId() != null) {
                    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(flow.getProcInsId()).singleResult();
                    if (processInstance != null) {
                        // 设置流程表单信息
                        if (processInstance != null && processInstance.getBusinessKey() != null && processInstance.getBusinessKey().contains(":")) {
                            String[] ss = processInstance.getBusinessKey().split(":");
                            flow.setBusinessTable(ss[0]);
                            flow.setBusinessId(ss[1]);
                        } else if (processInstance != null && processInstance.getBusinessKey() != null) {
                            flow.setBusinessId(processInstance.getBusinessKey());
                        }
                    }
                }

                // 如果是审核中,查询当前节点的流程节点扩展信息
                if (StringUtils.isNotEmpty(taskVo.getProcessDefKey()) && StringUtils.isNotEmpty(taskVo.getTaskDefinitionKey()) && StringUtils.isNotEmpty(currentUserId)) {
                    TaskDefExtensionRequest taskDefExtensionRequest = new TaskDefExtensionRequest();
                    taskDefExtensionRequest.setProcessDefId(taskVo.getProcessDefKey());
                    taskDefExtensionRequest.setTaskDefId(taskVo.getTaskDefinitionKey());
                    List<TaskDefExtension> taskDefExtensions = taskDefExtensionGateway.findList(taskDefExtensionRequest);
                    if (CollectionUtil.isNotEmpty(taskDefExtensions) && taskDefExtensions.size() == 1) {
                        String id = taskDefExtensions.get(0).getId();
                        TaskDefExtension taskDefExtension = taskDefExtensionGateway.get(id);
                        if (StringUtils.isNotEmpty(procInsId)) {
                            // 验证当前节点审核人是否是当前登录人
                            Boolean myAudit = flowTaskClientService.isMyAudit(procInsId, currentUserId);
                            if (!myAudit) {
                                taskDefExtension.setFlowButtonList(null);
                            }
                            // 获取当前节点的流程变量
                            Map<String, Object> variables = runtimeService.getVariables(procInsId);
                            // 获取下一步审批人信息
                            Map<String, Map<String, Object>> nextAssigneeOnTask = flowTaskClientService.getNextAssigneeOnTask(task, taskDefExtension.getProcessDefId(), variables);
                            // 判断是否为多实例
                            boolean isMultiInstance = false;
                            if (nextAssigneeOnTask != null) {
                                Map<String, Object> map = nextAssigneeOnTask.get("isMultiInstance");
                                if (map != null) {
                                    isMultiInstance = (boolean) map.get("isMultiInstance");
                                    nextAssigneeOnTask.remove("isMultiInstance");
                                }
                            }
                            if (isMultiInstance) {
                                // 如果是会签，则可手动选择会签人员
                                List<FlowAssigneeModel> assigneeChooses = flowTaskClientService.getMultiInstanceAssigneeChooses(nextAssigneeOnTask, variables);
                                taskDefExtension.setFlowAssigneeList(assigneeChooses);
                            } else {
                                List<FlowAssigneeModel> assigneeTypeVos = flowTaskClientService.getAssigneeTypeVos(nextAssigneeOnTask);
                                taskDefExtension.setFlowAssigneeList(assigneeTypeVos);
                            }
                        }

                        jsonObject.put("buttons", taskDefExtension.getFlowButtonList());
                        jsonObject.put("nextAssigneeOnTask", taskDefExtension.getFlowAssigneeList());
                    }
                }
            }

            // 获取流程XML上的表单KEY
            String formKey = null;
            if (processVo.getTask() != null) {
                flow.setTaskName(processVo.getTask().getName());
                flow.setTaskDefKey(processVo.getTask().getTaskDefinitionKey());
                flow.setProcInsId(procInsId);
                flow.setProcDefId(processVo.getTask().getProcessDefinitionId());
                flow.setProcDefKey(processVo.getTask().getProcessDefKey());
                flow.setTaskId(processVo.getTask().getId());
            } else {
                flow.setProcInsId(procInsId);
                flow.setProcDefId(processVo.getProcessDefinitionId());
            }

            String formType = "2";
            boolean formReadOnly = true;
            flow.setFormUrl(formKey);
            flow.setFormReadOnly(formReadOnly);
            flow.setFormType(formType);
            // 根据流程结束时间来判断流程是否完成审核
            if (processVo.getEndTime() == null) {
                flow.setStatus(FlowableConstant.TODO);
            } else {
                flow.setStatus(FlowableConstant.END);
            }
            jsonObject.put("flow", flow);
            return jsonObject;
        }
        return null;
    }

    /**
     * 查询流程实例状态
     *
     * @return ProcessVo
     */
    public ProcessVo queryProcessState(String processDefinitionId, String processInstanceId) {
        ProcessVo processVo = new ProcessVo();
        // 通过流程实例ID查询流程实例
        ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        if (pi != null) {
            if (pi.isSuspended()) {
                //挂起实例
                processVo.setProcessStatus(ProcessStatus.SUSPENDED);
                return processVo;
            } else {
                //执行实例
                processVo.setProcessStatus(ProcessStatus.WAITING);
                List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
                if (CollectionUtils.isEmpty(list)) {
                    return processVo;
                }
                Task currentTask = list.get(0);
                processVo.setTask(new TaskVo(currentTask));
                processVo.setTaskName(currentTask.getName());
                return processVo;
            }

        } else {
            HistoricProcessInstance pi2 = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
//            String endActivityId = pi2.getEndActi vityId ();
            HistoricActivityInstance historicActivityInstance = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).finished().orderByHistoricActivityInstanceEndTime().desc().list().stream()
                    .filter(activity -> activity.getActivityType().equals(BpmnXMLConstants.ELEMENT_TASK_USER)
                            || activity.getActivityType().equals(BpmnXMLConstants.ELEMENT_EVENT_END)
                            || activity.getActivityType().equals(BpmnXMLConstants.ELEMENT_EVENT_START)).distinct().collect(Collectors.toList()).get(0);
            Process process = repositoryService.getBpmnModel(processDefinitionId).getMainProcess();
            FlowNode flowElement = (FlowNode) process.getFlowElement(historicActivityInstance.getActivityId(), true);
            if (pi2 != null) {

                HistoricVariableInstance processStatusInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).variableName(FlowableConstant.PROCESS_STATUS_CODE).singleResult();

                if (pi2.getDeleteReason() == null && processStatusInstance == null) {
                    processVo.setProcessStatus(ProcessStatus.AGREE);
                    processVo.setTaskName(flowElement.getName());
                    return processVo;
                } else if (pi2.getDeleteReason() != null) {
                    processVo.setProcessStatus(ProcessStatus.STOP);
                    processVo.setTaskName(flowElement.getName());
                    processVo.setDeleteReason(pi2.getDeleteReason());
                    return processVo;
                } else {
                    int code = Integer.valueOf(processStatusInstance.getValue().toString());
                    ProcessStatus processStatus = ProcessStatus.value(code);
                    processVo.setProcessStatus(processStatus);
                    processVo.setTaskName(flowElement.getName());
                    return processVo;
                }

            } else {
                processVo.setProcessStatus(ProcessStatus.DELETED);
                processVo.setTaskName(flowElement.getName());
                processVo.setDeleteReason(pi2.getDeleteReason());
                return processVo;
            }

        }
    }
    public Map getDiagram(String processId) {
        Map m = new HashMap();
        try {
            String processDefId = "";
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            // 流程走完的不显示图
            if (pi == null) {
                processDefId = historyService
                        .createHistoricProcessInstanceQuery()
                        .processInstanceId(processId)
                        .singleResult()
                        .getProcessDefinitionId();
            } else {
                processDefId = pi.getProcessDefinitionId();
            }
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefId);
            List<HistoricActivityInstance> historyProcess = getHistoryProcess(processId);
            Set<String> activityIds = new LinkedHashSet<>();

            List<String> flows = new ArrayList<>();
            for (HistoricActivityInstance hi : historyProcess) {
                String activityType = hi.getActivityType();
                if (activityType.equals(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)
                        || activityType.equals(BpmnXMLConstants.ELEMENT_GATEWAY_EXCLUSIVE)) {
                    flows.add(hi.getActivityId());
                } else if (StringUtils.isNotBlank(hi.getAssignee())
                        && historyService.createHistoricTaskInstanceQuery().taskId(hi.getTaskId()).count()
                        != 0
                        || BpmnXMLConstants.ELEMENT_TASK_USER.equals(hi.getActivityType())
                        && hi.getEndTime() == null
                        || BpmnXMLConstants.ELEMENT_EVENT_START.equals(hi.getActivityType())
                        || BpmnXMLConstants.ELEMENT_EVENT_END.equals(hi.getActivityType())) {
                    activityIds.add(hi.getActivityId());
                }
            }
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(processId).list();
            for (Task task : tasks) {
                activityIds.add(task.getTaskDefinitionKey());
            }
            // 多语言流程图
            byte[] bpmnBytes = this.getBpmnXML(bpmnModel);

            m.put("bpmnXml", new String(bpmnBytes));
            m.put("flows", flows);
            m.put("activityIds", activityIds);
            m.put("name",bpmnModel.getSourceSystemId().split(".bpmn20.xml"));
            return m;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public byte[] getBpmnXML(BpmnModel bpmnModel) {
        for (Process process : bpmnModel.getProcesses()) {
            if (org.apache.commons.lang3.StringUtils.isNotEmpty(process.getId())) {

                Map<String, FlowElement> flowElementMap = process.getFlowElementMap();

                flowElementMap.forEach(
                        (key, value) -> {
                            String name = value.getName();
                        });

                char firstCharacter = process.getId().charAt(0);
                // no digit is allowed as first character
                if (Character.isDigit(firstCharacter)) {
                    process.setId("a" + process.getId());
                }
            }
        }

        return bpmnXMLConverter.convertToXML(bpmnModel);
    }
    /**
     * 任务历史
     *
     * @param processId 部署id
     */
    public List<HistoricActivityInstance> getHistoryProcess(String processId) {
        List<HistoricActivityInstance> list = historyService // 历史相关Service
                .createHistoricActivityInstanceQuery() // 创建历史活动实例查询
                .processInstanceId(processId) // 执行流程实例id
                .finished()
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .list();
        return list;
    }
    /**
     * 获取流转历史任务列表
     *
     * @param params 流程实例
     */
    public List<FlowDto> historicTaskList(Map<String, Object> params, Boolean isApp) throws Exception {
        String procInsId = MapUtils.getString(params, "procInsId");

        String orderBillcode = MapUtils.getString(params, "billcode");

        String proMark = MapUtils.getString(params, "proMark");
        String businessKey =  orderBillcode;
        String processDefId = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(procInsId)
                .processInstanceBusinessKey(businessKey)
                .singleResult()
                .getProcessDefinitionId();

        List<FlowDto> actList = new ArrayList<>();
        // 该审批流程所有的历史流程实例信息
        List<HistoricProcessInstance> historicProcessInstances = new ArrayList<>();

        if (isApp) {
            historicProcessInstances = historyService
                    .createHistoricProcessInstanceQuery()
                    .processDefinitionId(processDefId)
                    .processInstanceBusinessKey(businessKey)
                    .orderByProcessInstanceStartTime()
                    .desc()
                    .list();
        } else {
            historicProcessInstances = historyService
                    .createHistoricProcessInstanceQuery()
                    .processDefinitionId(processDefId)
                    .processInstanceBusinessKey(businessKey)
                    .orderByProcessInstanceStartTime()
                    .asc()
                    .list();
        }

        List<HistoricActivityInstance> list = null;

        for (int i = 0; i < historicProcessInstances.size(); i++) {
            HistoricProcessInstance processInstance = historicProcessInstances.get(i);
            List<HistoricActivityInstance> historicActivityInstances = historyService
                    .createHistoricActivityInstanceQuery()
                    .processDefinitionId(processDefId)
                    .processInstanceId(processInstance.getId())
                    .orderByHistoricActivityInstanceStartTime()
                    .asc()
                    .orderByHistoricActivityInstanceEndTime()
                    .asc()
                    .list();
            for (HistoricActivityInstance histIns : historicActivityInstances) {
                // 只显示开始节点和结束节点，并且执行人不为空的任务
                if (StringUtils.isNotEmpty(histIns.getAssignee()) && historyService.createHistoricTaskInstanceQuery().taskId(histIns.getTaskId()).count() != 0
                        || BpmnXMLConstants.ELEMENT_TASK_USER.equals(histIns.getActivityType())
                        && histIns.getEndTime() == null
                        || BpmnXMLConstants.ELEMENT_EVENT_START.equals(histIns.getActivityType())
                        || BpmnXMLConstants.ELEMENT_EVENT_END.equals(histIns.getActivityType())) {
                    // 获取流程发起人名称
                    FlowDto e = queryTaskState(histIns);
                    actList.add(e);
                }
            }
        }

        return actList;
    }

    /**
     * 查询任务节点的状态
     */
    public FlowDto queryTaskState(HistoricActivityInstance histIns) {
        FlowDto e = new FlowDto();
        e.setHistIns(histIns);
        e.setProcInsId(histIns.getProcessInstanceId());
        // 获取流程发起人名称
        if (BpmnXMLConstants.ELEMENT_EVENT_START.equals(histIns.getActivityType())) {
            List<HistoricProcessInstance> il = historyService
                    .createHistoricProcessInstanceQuery()
                    .processInstanceId(histIns.getProcessInstanceId())
                    .orderByProcessInstanceStartTime()
                    .asc()
                    .list();
            if (il.size() > 0) {
                if (StringUtils.isNotBlank(il.get(0).getStartUserId())) {
                    e.setAssigneeName(FlowableConstant.START_EVENT_COMMENT);
                    e.setAssignee(il.get(0).getStartUserId());
                }
            }
            TaskComment taskComment = new TaskComment();
            taskComment.setStatus(FlowableConstant.START_EVENT_LABEL);
//            taskComment.setMessage(FlowableConstant.START_EVENT_COMMENT);
            e.setComment(taskComment);
            e.setTaskName(FlowableConstant.START_EVENT_LABEL);
            return e;
        }
        if (BpmnXMLConstants.ELEMENT_EVENT_END.equals(histIns.getActivityType())) {
            TaskComment taskComment = new TaskComment();
            taskComment.setStatus(FlowableConstant.END_EVENT_LABEL);
//            taskComment.setMessage(FlowableConstant.END_EVENT_COMMENT);
            e.setAssigneeName(FlowableConstant.SYSTEM_EVENT_COMMENT);
            e.setComment(taskComment);
            e.setTaskName(FlowableConstant.END_EVENT_LABEL);
            return e;
        }
        // 获取任务执行人名称
        String taskId = histIns.getTaskId();
        if (StringUtils.isNotEmpty(histIns.getAssignee())) {
            e.setAssignee(histIns.getAssignee());
        } else {
            List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(taskId);
            if (org.flowable.editor.language.json.converter.util.CollectionUtils.isNotEmpty(identityLinksForTask)) {
                StringBuilder userIds = new StringBuilder();
                for (IdentityLink identityLink : identityLinksForTask) {
                    String userId = identityLink.getUserId();
                    userIds.append(userId).append(",");
                }
                e.setAssignee(userIds.substring(0, userIds.length() - 1));
            }
        }
        // 获取意见评论内容
        if (StringUtils.isNotBlank(taskId)) {
            List<TaskComment> commentList = new ArrayList<>();
            if (commentList.size() > 0) {
                TaskComment comment = commentList.get(commentList.size() - 1);
                e.setComment(comment);
            } else {
                e.setComment(new TaskComment());
            }
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
            BpmnModel bpmnModel = repositoryService.getBpmnModel(histIns.getProcessDefinitionId());
            FlowElement flowElement = bpmnModel.getFlowElement(historicTaskInstance.getTaskDefinitionKey());
            String name = flowElement.getName();
            e.setTaskName(name);
        }
        // 等待执行的任务
        if (histIns.getEndTime() == null) {
            TaskComment taskComment = new TaskComment();
            taskComment.setStatus(ActionType.WAITING.getStatus());
            e.setComment(taskComment);
        }

        if (!StringUtils.isEmpty(e.getComment().getType())) {
            switch (e.getComment().getType()) {
                case "_flow_agree":
                    e.getComment().setStatus(ActionType.AGREE.getStatus());
                    break;
                case "_flow_reject":
                    e.getComment().setStatus(ActionType.REJECT.getStatus());
                    break;
                case "_flow_stop":
                    e.getComment().setStatus(ActionType.STOP.getStatus());
                    break;
                case "_flow_commit":
                    e.getComment().setStatus(ActionType.COMMIT.getStatus());
                    break;
                case "_flow_revoke":
                    e.getComment().setStatus(ActionType.REVOKE.getStatus());
                    break;
                case "_flow_back":
                    e.getComment().setStatus(ActionType.BACK.getStatus());
                    break;
                default:
                    e.getComment().setStatus(ActionType.AGREE.getStatus());
                    break;
            }
        }

        return e;
    }
}
